package org.remixlab.gnutopic.data;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.TreeMap;

import org.apache.commons.lang3.StringEscapeUtils;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
import org.remixlab.gnutopic.pojo.ForumCategory;
import org.remixlab.gnutopic.pojo.ForumMessage;
import org.remixlab.gnutopic.pojo.ForumThread;

import au.com.bytecode.opencsv.CSVReader;

/**
 * 
 * Clase encargarda de obtener los hilos que 
 * se encuentran almacenados en archivos de texto
 * @author mauro
 */

public class CSVDataManager {
	
	private final int THREAD_ID_POS = 0;
	private final int THREAD_TOPIC_POS = 1;
	private final int THREAD_DATE_POS = 4;
	private final int THREAD_REPLY_NUMBER_POS=5;	//posición del número de mensajes que indican el mensaje inicial.
	private final int THREAD_USER_POS = 6;
	
	private final int MESSAGE_TITLE = 0;
	private final int MESSAGE_DATE = 3;
	private final int MESSAGE_USER_NAME = 1;
	private final int MESSAGE_USER_NICK_NAME = 1;
	private final int MESSAGE_TEXT_MESSAGE = 8;
	
	
	
	private String boardPath; 	//la ruta donde se encuentran los archivos con la informacion de los threads
	private String messagePath; //la ruta donde se encuentran los archivos con la información de los mensajes
	private String separator;	//caracter que indica el separador a usar
	
	private ArrayList<ForumCategory> categories;	//categorias del foro
	private TreeMap<Date, ForumThread> threads;		//hilos del foro ordeandos por fecha.
	
	private static CSVDataManager instance;	//Esta es la instancia de la clase
	
	private CSVDataManager(){
		
		boardPath = "/home/mauro/P5_Alpha_Archive/Boards";
		messagePath = "/home/mauro/P5_Alpha_Archive/Messages";
		separator = "|";
		
		//inicializo el arreglo..
		categories = new ArrayList<ForumCategory>();
		
		//aqui inicializo el treeMap de hilos...
		threads = new TreeMap<Date, ForumThread>();
	}
	
	public static CSVDataManager getInstance(){
		
		if(instance==null){
			//inicializo la insnacia
			instance = new CSVDataManager();
			
			//TODO: Falta leer estos parámetros de un archivo o de una bd.
			//por ahora lo inicializo por defecto...
			//instance.setBoardPath("/home/mauro/P5_Alpha_Archive/Boards");
			//instance.setMessagePath("/home/mauro/P5_Alpha_Archive/Messages");
			
			//instance.setBoardPath("F:\\mauro\\Universidad\\Postgrado\\2011-01\\seminario de investigacion 3\\Documentos Tesis\\P5_Alpha_Archive\\Boards");
			//instance.setMessagePath("F:\\mauro\\Universidad\\Postgrado\\2011-01\\seminario de investigacion 3\\Documentos Tesis\\P5_Alpha_Archive\\Messages");
			//instance.setSeparator("|");
			
		}
		
		return instance;
	}
	
	public String getBoardPath() {
		return boardPath;
	}

	public void setBoardPath(String boardPath) {
		this.boardPath = boardPath;
	}

	public String getMessagePath() {
		return messagePath;
	}

	public void setMessagePath(String messagePath) {
		this.messagePath = messagePath;
	}

	public String getSeparator() {
		return separator;
	}

	public void setSeparator(String separator) {
		this.separator = separator;
	}
	
	/**
	 * Método que consulta las categorías del foro.
	 * @return
	 */
	public ArrayList<ForumCategory> getCategories(){
		
		//verifico que el arreglo de categorías no sea nulo y que el tamaño sea menor o igual a cero..
		//eso indica que no he consultado las categorías antes...
		if(categories!=null && categories.size()<=0){
			
			if(boardPath!=null && boardPath!=""){
				
				//obtengo los archivos del directorio.
				File dir = new File(boardPath);
				
				//filtro los archivos por extensión (obtengo los .cat)
				
				OnlyExtFilter filter = new OnlyExtFilter("cat");
				
				//obtengo los archivos .cat
				String [] files = dir.list(filter);
				
				//variable que asigna un id a cada categoría.
				int i = 0;
				int j = 1;
				
				if(files!=null){
					//recorro los archivos y creo las categorias....
					for(String fileName : files){
						//aqui debo leer el archivo de categorías y crear cada una de ellas...
						
						//creo la categoría
						ForumCategory category = new ForumCategory();
						
						//leo el archivo
						File file = new File(boardPath+"/"+fileName);
						
						try {
							BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
							
							//indica si es la primera linea..
							boolean firstLine = true;
							
							String line = null;
							
							while ((line = bufferedReader.readLine()) != null) {
								if(firstLine){
									i++;
									
									//lleno el nombre de la categoría...
									category.setName(line);
									category.setId(i*100);
									
									j=1;
									
									//yacssdfasd
									firstLine = false;
									
								}else{
									if(!line.equalsIgnoreCase("") && !line.equalsIgnoreCase("\n")){
										//significa que es una sub categoria..
										
										//obtengo la sub categoría y trato de llenar los hilos..
										ForumCategory subCategory = getSubCategoryByName(line,category);
										if(subCategory!=null){
											//modifico el id de la categoría
											subCategory.setId((i*100)+j);
											
											//incremento el id en uno
											j++;
											
											//asigno la categoría al padre.
											category.addSubCategory(subCategory);
										}
									}
								}
							}
							
							//agrego la categoria a la lista
							categories.add(category);
							
						} catch (FileNotFoundException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}	
				}
			}
		}
		
		return categories;
	}
	
	/**
	 * 
	 * @return
	 */
	public TreeMap<Date, ForumThread> getThreads(){

		//significa que no se han lleando los arreglos.. obtengo las categorías
		//para llenar la lista de threads
		if(threads!=null && threads.size()<=0){
			getCategories();
		}
		
		return threads;
		
	}
	
	/**
	 * Obtiene las sub categorias por nombre...
	 * @param name
	 * @return
	 */
	private ForumCategory getSubCategoryByName(String name, ForumCategory category){
		ForumCategory result = null;
		
		//aqui abro el archivo .txt y obtengo los hilos para la sub categoría..
		
		try {
			InputStream in = new FileInputStream (boardPath+"/"+name+".txt");
			
			CSVReader reader = new CSVReader( new InputStreamReader( in, "UTF-8" ), '|');
			
			result = new ForumCategory(name);
			
			//agrego la categoría padre.
			result.setParentCategory(category);
			
			String [] nextLine;

			DateFormat formatter = new SimpleDateFormat("MM/dd/yy 'at' HH:mm:ss");
			
			while ((nextLine = reader.readNext()) != null) {
		        
				//creo el hilo...
				ForumThread thread = new ForumThread();
				
				//lleno los datos del hilo.
				thread.setId(nextLine[THREAD_ID_POS]);
				thread.setTopic(nextLine[THREAD_TOPIC_POS]);
				thread.setUser(nextLine[THREAD_USER_POS]);
				
				thread.setReplyNumber(Integer.parseInt(nextLine[THREAD_REPLY_NUMBER_POS]));
				
				//aqui debo leer los mensajes para el hilo...
				getMessages(thread);
				//ArrayList<ForumMessage> messages = getMessages(thread);
				
				//se asigno los mensajes al hilo.
				//thread.setMessages(messages);
				
				/*try {
					Date date = (Date)formatter.parse(nextLine[THREAD_DATE_POS]);
					thread.setDate(date);
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}*/
				
				//verifico que el hilo tenga fecha y mensajes.. antes de agregarlo
				//a la categoría.
				if (thread.getDate()!=null &&
					thread.getMessages()!=null && thread.getMessages().size()>0){
					
					//agrego el hilo a la categoría
					result.addThread(thread);
					
					//agrego la categoría al hilo
					thread.setCategory(result);
					
					//agrego el hilo al treeMap de hilos del foro.
					threads.put(thread.getDate(), thread);
					
					int messagesNumber = result.getMessagesNumber()+thread.getMessagesNumber();
					
					//actualizo el número de mensajes para esta categoría.
					result.setMessagesNumber(messagesNumber);
					
					//aqui agrego el hilo al treeMap de la categoría padre.
					category.addThread(thread);	
				}
				
				
				
		    }
			
			//actualizo el número de mensajes para la categoría padre.
			category.setMessagesNumber(category.getMessagesNumber()+result.getMessagesNumber());
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return result;
	}
	
	/**
	 * Método encargado de obtener los mensajes de un thread
	 * @param threadId
	 * @return
	 */
	private ArrayList<ForumMessage> getMessages(ForumThread thread){
		ArrayList<ForumMessage> result = new ArrayList<ForumMessage>();
		
		//se busca el archivo en el directorio de mensajes.
		try {
			InputStream in = new FileInputStream (messagePath+"/"+thread.getId()+".txt");
			
			CSVReader reader = new CSVReader(new InputStreamReader( in, "UTF-8" ), '|');
			
			String [] nextLine;
			
			DateFormat formatter = new SimpleDateFormat("MM/dd/yy 'at' HH:mm:ss");
			
			int i = 1;
			
			Date lastDate = null;
			
			while ((nextLine = reader.readNext()) != null) {
				if(nextLine.length>=MESSAGE_TEXT_MESSAGE){
					//System.out.println("threadId:"+threadId+" messageName: "+nextLine[MESSAGE_TITLE]+" user: "+nextLine[MESSAGE_USER_NICK_NAME]+" message: "+nextLine[MESSAGE_TEXT_MESSAGE]);
					
					//creo el mensaje.
					ForumMessage message = new ForumMessage();
					
					//guardo el id del thread
					message.setThreadId(thread.getId());
					
					//le asigno el título
					message.setTitle(nextLine[MESSAGE_TITLE]);
					
					//le asigno el texto
					String tempText = nextLine[MESSAGE_TEXT_MESSAGE];
					
					tempText = Jsoup.clean(tempText, Whitelist.basic());
					
					tempText = Jsoup.parse(tempText).text();
					
					tempText = StringEscapeUtils.escapeJava(tempText);
					
					message.setText(tempText);
					
					message.setMessageId(i);
					
					try {
						Date date = (Date)formatter.parse(nextLine[MESSAGE_DATE]);
						
						if(lastDate == null){
							lastDate = date;
						}else{
							if(lastDate.getTime()<date.getTime()){
								lastDate = date;
							}
						}
						
						//asigno la fecha.
						message.setDate(date);
						
					} catch (ParseException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					message.setUserName(nextLine[MESSAGE_USER_NAME]);
					
					//finalmente agrego el mensaje a la lista de mensajes
					result.add(message);
					
					i++;
				}
			}
			
			//asigno la fecha para el thread.
			thread.setDate(lastDate);
			
			//luego asigno los mensajes.
			thread.setMessages(result);
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return result;
	}
	
	
	/**
	 * Guardo la información resumida en un xml.
	 */
	public void createXml(){	
		//primero obtengo los hilos..
		this.getCategories();
		
		//valido que los hilos tengan datos
		if(categories!=null && categories.size()>0){
			
			try {
				
				File fileDir = new File("forum_processing.xml");
				 
				Writer out = new BufferedWriter(new OutputStreamWriter(
				new FileOutputStream(fileDir), "UTF8"));
		 
				out.append("<forum>\n");
				
				for (ForumCategory category : categories){
					
					out.append(category.toXml());
					out.flush();
				}
								
				out.append("</forum>\n");
				
				out.flush();
				
				out.close();
				
				
			}catch (UnsupportedEncodingException e){
				System.out.println(e.getMessage());
			}catch (IOException e) {
				System.out.println(e.getMessage());
			}catch (Exception e){
				System.out.println(e.getMessage());
			}
		}
	}
	
	/**
	 * Este método crea el documento xml necesario para realizar 
	 * el indice de los mensajes.
	 */
	public void createXmlDocument(){
		
		//primero obtengo los hilos..
		this.getThreads();
		
		//valido que los hilos tengan datos
		if(threads!=null && threads.size()>0){
			
			//inicializo el collectionThreads
			Collection<ForumThread> collectionThread = threads.values();
			
			//obtengo un iterador
			Iterator<ForumThread> iterator = collectionThread.iterator();
			
			try {
				
				File fileDir = new File("processing_index.xml");
				 
				Writer out = new BufferedWriter(new OutputStreamWriter(
				new FileOutputStream(fileDir), "UTF8"));
		 
				out.append("<add>\n");
				
				while(iterator.hasNext()){
					//obtengo el thread..
					ForumThread thread = iterator.next();
					
					out.append(thread.toDocXML());
					out.flush();
				}
				
				out.append("</add>\n");
				
				out.flush();
				
				out.close();
				
				
			}catch (UnsupportedEncodingException e){
				System.out.println(e.getMessage());
			}catch (IOException e) {
				System.out.println(e.getMessage());
			}catch (Exception e){
				System.out.println(e.getMessage());
			}
		}
		
	}
	
	//Método para probar la clase...
	public static void main (String [] args){
		
//		CSVDataManager.getInstance().createXmlDocument();
		
		CSVDataManager.getInstance().createXml();
		
//		CSVDataManager dataManager = new CSVDataManager();
//		
//		dataManager.setBoardPath("/home/mauro/P5_Alpha_Archive/Boards");
//		dataManager.setMessagePath("/home/mauro/P5_Alpha_Archive/Messages");
//		dataManager.setSeparator("|");
//		
//		ArrayList<ForumCategory> categories = dataManager.getCategories();
//		
//		for(ForumCategory category : categories){
//			System.out.println("Categoria: "+category);
//			
//			/*ArrayList<ForumCategory> subCategories = category.getSubCategories();
//			//obteniendo los threads para cada sub categoría....
//			
//			for(ForumCategory subCategory : subCategories){
//				ArrayList<ForumThread> threads = dataManager.getThreadsByCategory(subCategory);	
//			}*/
//			
//			
//			
//		}
		
	}
}
